# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: jaewon

import logging

from coin.exchange.base.order_gateway import OrderType, OrderSide
from coin.exchange.okex_futures.kr_rest.futures_product import OkexFuturesProduct
from coin.exchange.okex_futures_swap.order_gateway import OkexFuturesSwapOrderGateway
from coin.exchange.okex_v3.kr_rest.native_private_client import OkexFuturesOrderSide
from coin.strategy.executor.passive.simple_mm_executor import \
    SimpleMMExecutor
from coin.strategy.executor.util.traits import (order_logger, FuturesExecutorTraits)
from coin.strategy.executor.aggressive.aggressive_executor import \
    AggressiveExecutor


class OkexFuturesSwapSimpleTraits(FuturesExecutorTraits):
  BuyOrderSide = [OkexFuturesOrderSide.BUY_OPEN, OkexFuturesOrderSide.BUY_CLOSE]
  SellOrderSide = [OkexFuturesOrderSide.SELL_OPEN, OkexFuturesOrderSide.SELL_CLOSE]

  def __init__(self, executor_config, order_gateway, logger=None):
    logger = logger or logging.getLogger('OkexFuturesSimpleMMExecutor')
    super().__init__(executor_config, order_gateway, logger)

  def _get_position(self, product):
    return self.og.get_position(product)

  def _submit_order_impl(self, product, price, qty, order_side, post_only=None):
    if not self.og.submittable():
      self._logger.warning('Skip submission: %s %f@%f' % (product, qty, price))
      return False

    sell_working_close_order_qty = 0
    buy_working_close_order_qty = 0
    for order in self._og.get_working_order():
      if order.product != self.product:
        continue
      if order.order_side == OkexFuturesOrderSide.SELL_CLOSE:
        sell_working_close_order_qty += order.qty
      elif order.order_side == OkexFuturesOrderSide.BUY_CLOSE:
        buy_working_close_order_qty += order.qty
    position = self.og.og_info.get_product_position_proto(product)
    short_position = abs(position.available_short_position)
    long_position = abs(position.available_long_position)
    if self.config.HasField('leverage'):
      leverage = self.config.leverage
    else:
      leverage = 10
    order_type = OrderType.LIMIT
    if order_side == OrderSide.SELL:
      if long_position >= max(self.config.maintain_open_pos, qty) + sell_working_close_order_qty:
        qty = min(qty, long_position)
        self._og.submit(product,
                        price,
                        qty,
                        OkexFuturesOrderSide.SELL_CLOSE,
                        order_type,
                        leverage=leverage,
                        post_only=post_only)
        self._logger.info('SELL_CLOSE  %s %f@%.3f', product, qty, price)
      else:
        self._og.submit(product,
                        price,
                        qty,
                        OkexFuturesOrderSide.SELL_OPEN,
                        order_type,
                        leverage=leverage,
                        post_only=post_only)
        self._logger.info('SELL_OPEN  %s %f@%.3f', product, qty, price)
    elif order_side == OrderSide.BUY:
      if short_position >= max(self.config.maintain_open_pos, qty) + buy_working_close_order_qty:
        qty = min(qty, short_position)
        self._og.submit(product,
                        price,
                        qty,
                        OkexFuturesOrderSide.BUY_CLOSE,
                        order_type,
                        leverage=leverage,
                        post_only=post_only)
        self._logger.info('BUY_CLOSE  %s %f@%.3f', product, qty, price)
      else:
        self._og.submit(product,
                        price,
                        qty,
                        OkexFuturesOrderSide.BUY_OPEN,
                        order_type,
                        leverage=leverage,
                        post_only=post_only)
        self._logger.info('BUY_OPEN  %s %f@%.3f', product, qty, price)
    else:
      raise ValueError('Unknown order side: %s' % order_side)

  @order_logger
  def _submit_order(self, product, price, qty, order_side):
    self._submit_order_impl(product, price, qty, order_side)

  @order_logger
  def _submit_pass_order(self, product, price, qty, order_side):
    self._submit_order_impl(product, price, qty, order_side, post_only=self.config.post_only)

  def _cancel_orders_impl(self, order_id_list):
    self.og.cancel_multiple(order_id_list)


class OkexFuturesSwapSimpleMMExecutor(SimpleMMExecutor):
  ProductType = OkexFuturesProduct
  OGType = OkexFuturesSwapOrderGateway
  Traits = OkexFuturesSwapSimpleTraits


class OkexFuturesSwapSimpleAggExecutor(AggressiveExecutor):
  ProductType = OkexFuturesProduct
  OGType = OkexFuturesSwapOrderGateway
  Traits = OkexFuturesSwapSimpleTraits
